﻿using Microsoft.IdentityModel.Tokens;
using NET6.WebAPI.Configs;
using NET6.WebAPI.Dtos.Tool;
using NET6.WebAPI.Extensions;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace NET6.WebAPI.Helpers
{
    public static class JwtHelper
    {
        /// <summary>
        /// 获取令牌和刷新令牌
        /// </summary>
        /// <param name="config">配置实例</param>
        /// <param name="claims">用户属性</param>
        /// <returns></returns>
        public static JwtDto GetTokenAndRefreshToken(IConfiguration config, IEnumerable<Claim> claims)
        {
            var expires = config[AppsettingsConfig.Expires].ToInt();
            var refreshExpires = config[AppsettingsConfig.RefreshExpires].ToInt();

            var jwt = new JwtDto
            {
                ExpiresTime = DateTime.Now.AddMinutes(expires),
                RefreshExpiresTime = DateTime.Now.AddDays(refreshExpires)
            };

            var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config[AppsettingsConfig.SigningKey]));

            var token = new JwtSecurityToken(
                config[AppsettingsConfig.Issuer],
                config[AppsettingsConfig.Audience],
                claims,
                expires: DateTime.Now.AddMinutes(expires),
                signingCredentials: new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256) // 指定加密方式
            );

            var refreshToken = new JwtSecurityToken(
                config[AppsettingsConfig.RefreshIssuer],
                config[AppsettingsConfig.RefreshAudience],
                claims: claims,
                expires: DateTime.Now.AddDays(refreshExpires),
                signingCredentials: new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256) // 指定加密方式
            );

            var handler = new JwtSecurityTokenHandler();
            jwt.Token = handler.WriteToken(token);
            jwt.RefreshToken = handler.WriteToken(refreshToken);

            return jwt;
        }

        /// <summary>
        /// 获取 Jwt 令牌验证参数
        /// </summary>
        /// <param name="config">配置实例</param>
        /// <returns></returns>
        public static TokenValidationParameters GetTokenValidationParms(IConfiguration config)
        {
            var signingKey = Encoding.UTF8.GetBytes(config[AppsettingsConfig.SigningKey]);

            return new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateIssuerSigningKey = true,
                ValidateLifetime = true,
                ValidIssuer = config[AppsettingsConfig.Issuer],
                ValidAudience = config[AppsettingsConfig.Audience],
                IssuerSigningKey = new SymmetricSecurityKey(signingKey),
                ClockSkew = TimeSpan.Zero // 不设置会推迟 300 秒过期, 可直接点击属性看源码
            };
        }

        /// <summary>
        /// 验证 Jwt 刷新令牌
        /// </summary>
        /// <param name="config">配置实例</param>
        /// <param name="refreshToken">刷新令牌</param>
        /// <returns>用户属性</returns>
        public static IEnumerable<Claim> ValidateRefreshToken(IConfiguration config, string refreshToken)
        {
            var signingKey = Encoding.UTF8.GetBytes(config[AppsettingsConfig.SigningKey]);

            var handler = new JwtSecurityTokenHandler();
            var parms = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateIssuerSigningKey = true,
                ValidateLifetime = true,
                ValidIssuer = config[AppsettingsConfig.RefreshIssuer],
                ValidAudience = config[AppsettingsConfig.RefreshAudience],
                IssuerSigningKey = new SymmetricSecurityKey(signingKey),
            };

            try
            {
                var principal = handler.ValidateToken(refreshToken, parms, out _);
                return principal.Claims;
            }
            catch (Exception)
            {
                return Enumerable.Empty<Claim>(); // IEnumerable 实例是不可变的, 仅用于遍历
            }
        }
    }
}
